#!/usr/bin/env python3

from enum import Enum

import abc
import typing as tp
import pydantic as pd

__all__ = ["Student", "Adminstrator", "Course", "user_deserialize", "course_deserialize"]

class BaseItem(pd.BaseModel):
  id:   str
  name: str
  type: str = None

  @pd.model_validator(mode="after")
  def _set_type(self):
    self.type = self.__class__.__name__
    return self

_classes: dict[str, type[BaseItem]] = {}
def item_class(cls: tp.Type[BaseItem]):
  _classes[cls.__name__] = cls
  return cls

class User(BaseItem, metaclass=abc.ABCMeta):
  username: str
  password: pd.constr(min_length=5, max_length=20)

@item_class
class Adminstrator(User):
  pass

@item_class
class Student(User):
  class StudentSex(str, Enum):
    male   = "male"
    female = "female"
  sex: StudentSex = None

  @pd.field_validator("id")
  def _check_student_id(cls, value):
    if not all(v.isdigit() for v in value):
      raise ValidationError("Students' id string must be all digits!")
    return value

@item_class
class Course(BaseItem):
  pass

def user_deserialize(login_info, user_info) -> User:
  return _classes.get(login_info["type"])(**login_info, **user_info)

def course_deserialize(course_info) -> tp.List[Course]:
  return [Course(**c) for c in course_info]
